/**
 * 深度克隆对象
 * @param source 待克隆对象
 * @param hash 循环引用缓存
 * @returns 克隆后的对象
 * @example
 * const obj = { a: 1, b: { c: 2 } };
 * const clonedObj = deepClone(obj);
 * console.log(clonedObj); // { a: 1, b: { c: 2 } }
 * console.log(clonedObj === obj); // false
 */

type Cloneable =
  | Date
  | RegExp
  | Set<unknown>
  | Map<unknown, unknown>
  | Array<unknown>
  | Function
  | object

const cloneStrategies = {
  Date: (value: Date) => new Date(value),
  RegExp: (value: RegExp) => {
    const cloned = new RegExp(value.source, value.flags)
    cloned.lastIndex = value.lastIndex
    return cloned
  },
  Set: <T>(value: Set<T>, clone: (v: T) => T) => {
    const cloned = new Set<T>()
    value.forEach(v => cloned.add(clone(v)))
    return cloned
  },
  Map: <K, V>(value: Map<K, V>, clone: <T>(v: T) => T) => {
    const cloned = new Map<K, V>()
    value.forEach((v, k) => cloned.set(clone(k), clone(v)))
    return cloned
  },
  Array: <T>(value: T[], clone: (v: T) => T) => {
    return value.map(v => clone(v))
  },
  Function: (value: Function) => {
    const cloned = function (this: unknown, ...args: unknown[]) {
      return value.apply(this, args)
    }
    Object.assign(cloned, value)
    return cloned
  }
}

export function deepClone<T extends Cloneable>(
  source: T,
  hash = new WeakMap<object, unknown>()
): T {
  // 处理 Vue 响应式对象
  const rawValue = isReactive(source) ? toRaw(source) : source

  // 基础类型直接返回
  if (typeof rawValue !== 'object' || rawValue === null) {
    return rawValue as T
  }

  // 处理循环引用
  if (hash.has(rawValue)) {
    return hash.get(rawValue) as T
  }

  // 获取原型链
  const proto = Object.getPrototypeOf(rawValue)

  // 处理特殊对象类型
  let cloned: any
  switch (true) {
    case rawValue instanceof Date:
      cloned = cloneStrategies.Date(rawValue)
      break
    case rawValue instanceof RegExp:
      cloned = cloneStrategies.RegExp(rawValue)
      break
    case rawValue instanceof Set:
      cloned = cloneStrategies.Set(rawValue as Set<unknown>, (v: any) => deepClone(v, hash))
      break
    case rawValue instanceof Map:
      cloned = cloneStrategies.Map(rawValue as Map<unknown, unknown>, (v: any) =>
        deepClone(v, hash)
      )
      break
    case Array.isArray(rawValue):
      cloned = cloneStrategies.Array(rawValue, v => deepClone(v, hash))
      break
    case typeof rawValue === 'function':
      cloned = cloneStrategies.Function(rawValue as Function)
      break
    default:
      cloned = Object.create(proto)
  }

  // 缓存克隆结果
  hash.set(rawValue, cloned)

  // 克隆所有属性（包括 Symbol 属性）
  Reflect.ownKeys(rawValue).forEach(key => {
    if (Object.prototype.hasOwnProperty.call(rawValue, key)) {
      const descriptor = Object.getOwnPropertyDescriptor(rawValue, key)
      if (descriptor) {
        Object.defineProperty(
          cloned,
          key,
          Object.assign(descriptor, {
            value: deepClone((rawValue as Record<PropertyKey, any>)[key], hash)
          })
        )
      }
    }
  })

  return cloned as T
}

export default deepClone
